package cn.icybear.orzjisp.orzvm;

import java.util.HashMap;

import cn.icybear.orzjisp.Identifier;
import cn.icybear.orzjisp.bytefile.CodeSection;

public class OrzFrame {
	public HashMap<Identifier, Object> localMap = new HashMap<Identifier, Object>();
	public OrzFrame parentFrame;
	public HashMap<Identifier, Object> closuerMap;
	public CodeSection codeSection;

	public OrzVMException newVMException(String txt, Throwable co)
			throws OrzVMException {
		int offset = codeSection.getCodeOffset();
		int line = codeSection.getCodeLineNumber(offset);
		return new OrzVMException(
				String.format("At code_offset:%d line:%d : %s", offset, line, txt),
				co);
	}

	public <T> T getVarible(Identifier id, Class<T> cls) throws OrzVMException {
		Object o = getVarible(id);
		if (o == null)
			return null;
		if (cls.isInstance(o)) {
			return cls.cast(o);
		} else {
			throw newVMException("Type error:" + cls.getSimpleName()
					+ " can't cast to " + o.getClass().getSimpleName(), null);
		}
	}

	public Object getVarible(Identifier id) throws OrzVMException {
		if (closuerMap != null && closuerMap.containsKey(id)) {
			return closuerMap.get(id);
		} else if (localMap.containsKey(id)) {
			return localMap.get(id);
		} else if (parentFrame != null) {
			return parentFrame.getVarible(id);
		} else {
			System.out.println("Closure:" + closuerMap);
			throw newVMException("Varible not binded : " + id, null);
		}
	}

	public boolean containsVarible(Identifier id) {
		if (closuerMap != null && closuerMap.containsKey(id)) {
			return true;
		} else if (localMap.containsKey(id)) {
			return true;
		} else if (parentFrame != null) {
			return parentFrame.containsVarible(id);
		} else {
			return false;
		}
	}

	public void bindVarible(Identifier id, Object val) {
		if (closuerMap != null && closuerMap.containsKey(id)) {
			closuerMap.put(id, val);
		} else if (parentFrame != null && parentFrame.containsVarible(id)) {
			parentFrame.bindVarible(id, val);
		} else {
			localMap.put(id, val);
		}
	}
}
